using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.IO.Compression;
using System.Xml;
using System.Xml.Serialization;
using System.Runtime.Serialization.Json;
using System.Runtime.Serialization.Formatters.Binary;
using System.Reflection;

namespace Toenda.Foundation.Serialization {
	/// <summary>
	/// Implements the Serialize class
	/// </summary>
	public static class Serialize {
		///// <summary>
		///// Get the memory size of an object
		///// </summary>
		///// <param name="value"></param>
		///// <returns></returns>
		//public static long SizeOf(object value) {
		//    RuntimeTypeHandle th = value.GetType().TypeHandle;
		//    int size = 0;

		//    unsafe {
		//        size = *(*(int**)&th + 1);
		//    }

		//    return (long)size;
		//}

		/// <summary>
		/// Serialize a object to a byte array
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="value"></param>
		/// <returns></returns>
		public static byte[] ToBinary<T>(T value) {
			MemoryStream stream = new MemoryStream();
			UTF8Encoding utf8e = new UTF8Encoding();

			using(XmlTextWriter xmlSink = new XmlTextWriter(stream, utf8e)) {
				BinaryFormatter bifo = new BinaryFormatter();

				using(GZipStream gzStream = new GZipStream(stream, CompressionMode.Compress)) {
					bifo.Serialize(gzStream, value);
				}
			}

			return stream.ToArray();
		}

		/// <summary>
		/// De-Serialize a byte array to a object
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="value"></param>
		/// <returns></returns>
		public static T FromBinary<T>(byte[] value) {
			T obj = default(T);

			using(MemoryStream stream = new MemoryStream(value)) {
				BinaryFormatter bifo = new BinaryFormatter();

				using(GZipStream gzStream = new GZipStream(stream, CompressionMode.Decompress)) {
					obj = (T)bifo.Deserialize(gzStream);
				}
			}

			return obj;
		}

		/// <summary>
		/// Serialize a object to a xml string
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="value"></param>
		/// <returns></returns>
		public static string ToXmlString<T>(T value) {
			XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
			MemoryStream stream = new MemoryStream();
			UTF8Encoding utf8e = new UTF8Encoding();
			XmlTextWriter xmlSink = new XmlTextWriter(stream, utf8e);
			xmlSerializer.Serialize(xmlSink, value);

			byte[] utf8EncodedData = stream.ToArray();
			return utf8e.GetString(utf8EncodedData);
		}

		/// <summary>
		/// De-Serialize a xml string to a object
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="value"></param>
		/// <returns></returns>
		public static T FromXmlString<T>(string value) {
			XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
			byte[] byteArray = Encoding.ASCII.GetBytes(value);
			MemoryStream stream = new MemoryStream(byteArray);

			return (T)xmlSerializer.Deserialize(stream);
		}

		/// <summary>
		/// Serialize a object to a json string
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="value"></param>
		/// <returns></returns>
		public static string ToJsonString<T>(T value) {
			DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
			MemoryStream ms = new MemoryStream();
			serializer.WriteObject(ms, value);
			return Encoding.Default.GetString(ms.ToArray());
		}

		/// <summary>
		/// De-Serialize a json string to a object
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="value"></param>
		/// <returns></returns>
		public static T FromJsonString<T>(string value) {
			MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(value));
			DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
			object returnObj = serializer.ReadObject(ms);
			ms.Close();

			return (T)returnObj;
		}
	}
}
